import { Game_BattlerBase } from "@/objects/GameBattlerBase.js";
import { Game_ActionResult } from "@/objects/GameActionResult.js";
import { Game_Action } from "@/objects/GameAction.js";

import { BattleManager } from "@/managers/BattleManager.js";
import { DataManager } from "@/managers/DataManager.js";
import { SoundManager } from "@/managers/SoundManager.js";

export class Game_Battler extends Game_BattlerBase {
    constructor() {
        super();
        this._actions = [];
        this._speed = 0;
        this._result = new Game_ActionResult();
        this._actionState = '';
        this._lastTargetIndex = 0;
        this._animations = [];
        this._damagePopup = false;
        this._effectType = null;
        this._motionType = null;
        this._weaponImageId = 0;
        this._motionRefresh = false;
        this._selected = false;
    }

    clearDamagePopup() {
        this._damagePopup = false;
    }

    clearWeaponAnimation() {
        this._weaponImageId = 0;
    }

    clearEffect() {
        this._effectType = null;
        //this.emit('effect-clear');
    }

    clearMotion() {
        this._motionType = null;
        this._motionRefresh = false;
    }

    requestEffect(effectType) {
        if(effectType!=this._effectType){
            let oldeffect=this._effectType;
            this._effectType = effectType;
            //this.emit('effect-changed',effectType,oldeffect);
        }
    }

    requestMotion(motionType) {
        if(motionType!=this._motionType){
            let oldmotion=this._motionType;
            this._motionType = motionType;
            //this.emit('motion-changed',motionType,oldmotion);
        }
    }

    requestMotionRefresh() {
        this._motionRefresh = true;
    }

    select() {
        this._selected = true;
        //this.emit('selected');
    }

    deselect() {
        this._selected = false;
        //this.emit('deselected');
    }

    isAnimationRequested() {
        return this._animations.length > 0;
    }

    isDamagePopupRequested() {
        return this._damagePopup;
    }

    isEffectRequested() {
        return !!this._effectType;
    }

    isMotionRequested() {
        return !!this._motionType;
    }

    isWeaponAnimationRequested() {
        return this._weaponImageId > 0;
    }

    isMotionRefreshRequested() {
        return this._motionRefresh;
    }

    isSelected() {
        return this._selected;
    }

    effectType() {
        return this._effectType;
    }

    motionType() {
        return this._motionType;
    }

    weaponImageId() {
        return this._weaponImageId;
    }

    shiftAnimation() {
        return this._animations.shift();
    }

    startAnimation(animationId, mirror, delay) {
        let data = { animationId: animationId, mirror: mirror, delay: delay };
        this._animations.push(data);
        this.emit('animation',data);
    }

    startDamagePopup() {
        this._damagePopup = true;
        //this.emit('damage-popup');
        this.clearDamagePopup();
        this.clearResult();
    }

    startWeaponAnimation(weaponImageId) {
        this._weaponImageId = weaponImageId;
        //this.emit('animation-weapon',weaponImageId);
    }

    action(index) {
        return this._actions[index];
    }

    setAction(index, action) {
        this._actions[index] = action;
    }

    numActions() {
        return this._actions.length;
    }

    clearActions() {
        this._actions = [];
    }

    result() {
        return this._result;
    }

    clearResult() {
        this._result.clear();
    }

    refresh() {
        super.refresh();
        if (this.hp == 0) {
            this.addState(this.deathStateId());
        } else {
            this.removeState(this.deathStateId());
        }
    }

    addState(stateId) {
        if (this.isStateAddable(stateId)) {
            if (!this.isStateAffected(stateId)) {
                this.addNewState(stateId);
                this.refresh();
            }
            this.resetStateCounts(stateId);
            this._result.pushAddedState(stateId);
        }
    }

    isStateAddable(stateId) {
        return (this.isAlive() && $dataStates[stateId] &&
                !this.isStateResist(stateId) &&
                !this._result.isStateRemoved(stateId) &&
                !this.isStateRestrict(stateId));
    }

    isStateRestrict(stateId) {
        return $dataStates[stateId].removeByRestriction && this.isRestricted();
    }

    onRestrict() {
        super.onRestrict();
        this.clearActions();
        this.states().forEach(state=>{
            if (state.removeByRestriction) {
                this.removeState(state.id);
            }
        });
    }

    removeState(stateId) {
        if (this.isStateAffected(stateId)) {
            if (stateId == this.deathStateId()) {
                this.revive();
            }
            this.eraseState(stateId);
            this.refresh();
            this._result.pushRemovedState(stateId);
        }
    }

    escape() {
        if ($gameParty.inBattle()) {
            this.hide();
        }
        this.clearActions();
        this.clearStates();
        SoundManager.playEscape();
    }

    addBuff(paramId, turns) {
        if (this.isAlive()) {
            this.increaseBuff(paramId);
            if (this.isBuffAffected(paramId)) {
                this.overwriteBuffTurns(paramId, turns);
            }
            this._result.pushAddedBuff(paramId);
            this.refresh();
        }
    }

    addDebuff(paramId, turns) {
        if (this.isAlive()) {
            this.decreaseBuff(paramId);
            if (this.isDebuffAffected(paramId)) {
                this.overwriteBuffTurns(paramId, turns);
            }
            this._result.pushAddedDebuff(paramId);
            this.refresh();
        }
    }

    removeBuff(paramId) {
        if (this.isAlive() && this.isBuffOrDebuffAffected(paramId)) {
            this.eraseBuff(paramId);
            this._result.pushRemovedBuff(paramId);
            this.refresh();
        }
    }

    removeBattleStates() {
        this.states().forEach(state=> {
            if (state.removeAtBattleEnd) {
                this.removeState(state.id);
            }
        });
    }

    removeAllBuffs() {
        for (let i = 0; i < this.buffLength(); i++) {
            this.removeBuff(i);
        }
    }

    removeStatesAuto(timing) {
        this.states().forEach(state=>{
            if (this.isStateExpired(state.id) && state.autoRemovalTiming == timing) {
                this.removeState(state.id);
            }
        });
    }

    removeBuffsAuto() {
        for (let i = 0; i < this.buffLength(); i++) {
            if (this.isBuffExpired(i)) {
                this.removeBuff(i);
            }
        }
    }

    removeStatesByDamage() {
        let states=this.states();
        for(let state of states){
            if (state.removeByDamage && Math.round(Math.random()*100) < state.chanceByDamage) {
                this.removeState(state.id);
            }
        }
    }

    makeActionTimes() {
        return this.actionPlusSet().reduce((r, p)=>Math.random() < p ? r + 1 : r, 1);
    }

    makeActions() {
        this.clearActions();
        if (this.canMove()) {
            let actionTimes = this.makeActionTimes();
            this._actions = [];
            for (let i = 0; i < actionTimes; i++) {
                this._actions.push(new Game_Action(this));
            }
        }
    }

    speed() {return this._speed;}

    makeSpeed() {
        this._speed=Infinity;
        for(let action of this._actions){
            let speed=action.speed();
            if(this._speed>speed){
                this._speed=speed;
            }
        }
        if(this._speed==Infinity){
            this._speed=0;
        }
    }

    currentAction() {
        return this._actions[0];
    }

    shiftAction(){
        return this._actions.shift();
    }

    removeCurrentAction() {
        this._actions.shift();
    }

    setLastTarget(target) {
        if (target) {
            this._lastTargetIndex = target.index();
        } else {
            this._lastTargetIndex = 0;
        }
    }

    forceAction(skillId, targetIndex) {
        this.clearActions();
        let action = new Game_Action(this, true);
        action.setSkill(skillId);
        if (targetIndex == -2) {
            action.setTarget(this._lastTargetIndex);
        } else if (targetIndex == -1) {
            action.decideRandomTarget();
        } else {
            action.setTarget(targetIndex);
        }
        this._actions.push(action);
    }

    useItem(item) {
        if (DataManager.isSkill(item)) {
            this.paySkillCost(item);
        } else if (DataManager.isItem(item)) {
            this.consumeItem(item);
        }
    }

    consumeItem(item) {
        $gameParty.consumeItem(item);
    }

    gainHp(value) {
        this._result.hpDamage = -value;
        this._result.hpAffected = true;
        this.setHp(this._hp + value);
    }

    gainMp(value) {
        this._result.mpDamage = -value;
        this.setMp(this.mp + value);
    }

    gainTp(value) {
        this._result.tpDamage = -value;
        this.setTp(this.tp + value);
    }

    gainSilentTp(value) {
        this.setTp(this.tp + value);
    }

    initTp() {
        this.setTp(Math.round(Math.random()*25));
    }

    clearTp() {
        this.setTp(0);
    }

    chargeTpByDamage(damageRate) {
        let value = Math.floor(50 * damageRate * this.tcr);
        this.gainSilentTp(value);
    }

    regenerateHp() {
        let value = Math.floor(this.mhp * this.hrg);
        value = Math.max(value, -this.maxSlipDamage());
        if (value !== 0) {
            this.gainHp(value);
        }
    }

    maxSlipDamage() {
        return $dataSystem.optSlipDeath ? this.hp : Math.max(this.hp - 1, 0);
    }

    regenerateMp() {
        let value = Math.floor(this.mmp * this.mrg);
        if (value !== 0) {
            this.gainMp(value);
        }
    }

    regenerateTp() {
        let value = Math.floor(100 * this.trg);
        this.gainSilentTp(value);
    }

    regenerateAll() {
        if (this.isAlive()) {
            this.regenerateHp();
            this.regenerateMp();
            this.regenerateTp();
        }
    }

    onBattleStart() {
        this.setActionState('undecided');
        this.clearMotion();
        if (!this.isPreserveTp()) {
            this.initTp();
        }
    }

    onAllActionsEnd() {
        this.clearResult();
        this.removeStatesAuto(1);
        this.removeBuffsAuto();
    }

    onTurnEnd() {
        this.clearResult();
        this.regenerateAll();
        if (!BattleManager.isForcedTurn()) {
            this.updateStateTurns();
            this.updateBuffTurns();
        }
        this.removeStatesAuto(2);
    }

    onBattleEnd() {
        this.clearResult();
        this.removeBattleStates();
        this.removeAllBuffs();
        this.clearActions();
        if (!this.isPreserveTp()) {
            this.clearTp();
        }
        this.appear();
    }

    onDamage(value) {
        this.removeStatesByDamage();
        this.chargeTpByDamage(value / this.mhp);
    }

    setActionState(actionState) {
        if(actionState!=this._actionState){
            let oldAState=this._actionState;
            this._actionState = actionState;
            //this.emit('astate-changed',actionState,oldAState);
        }
        //this.requestMotionRefresh();
    }

    isUndecided() {return this._actionState == 'undecided';}
    isInputting() {return this._actionState == 'inputting';}
    isWaiting() {return this._actionState == 'waiting';}
    isActing() {return this._actionState == 'acting';}

    isChanting() {
        if (this.isWaiting()) {
            return this._actions.some(action=>action.isMagicSkill());
        }
        return false;
    }

    isGuardWaiting() {
        if (this.isWaiting()) {
            return this._actions.some(function(action) {
                return action.isGuard();
            });
        }
        return false;
    }

    performActionStart(action) {
        if (!action.isGuard()) {
            this.setActionState('acting');
        }
    }

    performAction(action) {
    }

    performActionEnd() {
        this.setActionState('done');
    }

    performDamage() {
    }

    performMiss() {
        SoundManager.playMiss();
    }

    performRecovery() {
        SoundManager.playRecovery();
    }

    performEvasion() {
        SoundManager.playEvasion();
    }

    performMagicEvasion() {
        SoundManager.playMagicEvasion();
    }

    performCounter() {
        SoundManager.playEvasion();
    }

    performReflection() {
        SoundManager.playReflection();
    }

    performSubstitute(target) {
    }

    performCollapse() {
    }
}